/*
 * gui.c
 *
 *  Created on: 2023年9月10日
 *      Author: Cheney.Tan
 */

#include "gui.h"
#include "oled.h"
#include "hal_key.h"
#include "gui_pictures.h"
#include "gui_pages.h"
#include "string.h"
#include "device_common.h"

#ifdef NULL
#undef NULL
#define NULL 0
#endif

/**
 * GUI configuration
 */
#define GUI_REFRESH_INTERVAL	500	/* 500ms 刷新一遍 */

#define GUI_PAGE_CNT_MAX		10
#define GUI_PAGE_ITEM_CNT_MAX	10

static t_gui_page *current_page = NULL, *new_page = NULL;

void key_center_event_callback(key_event_enum e)
{
	switch (e)
	{
	case KEY_EVENT_SHORT_PRESS:

		if (gui_page_idle == current_page)
		{
			gui_page_change_to(gui_page_main);
		}
		else if (NULL != current_page->item_current)
		{
			/**
			 *  标记当前所选控件产生按下事件，供应用程序处理
			 */
			current_page->item_current->status.event_type = 1;
		}
		break;

	case KEY_EVENT_LONG_PRESS_RELEASED:

		break;

	default:
		break;
	}
}

void key_left_event_callback(key_event_enum e)
{
	switch (e)
	{
	case KEY_EVENT_SHORT_PRESS:

		/**
		 * 选择当前page的上一个控件
		 */
		gui_page_obj_prev(current_page);

		break;

	case KEY_EVENT_LONG_PRESS_HOLD:
		gui_page_change_to(current_page->parent);
		break;

	case KEY_EVENT_LONG_PRESS_RELEASED:
		//gui_page_change_to(current_page->parent);
		break;

	default:
		break;
	}
}

void key_right_event_callback(key_event_enum e)
{
	switch (e)
	{
	case KEY_EVENT_SHORT_PRESS:

		/**
		 * 选择当前page的下一个控件
		 */
		gui_page_obj_next(current_page);

		break;

	case KEY_EVENT_LONG_PRESS_RELEASED:

		break;

	default:
		break;
	}
}

int32_t gui_init(void)
{
	/**
	 * 屏幕 & 按键初始化
	 */
	OLED_Init();
	OLED_ColorTurn(0);	//0正常显示，1 反色显示
	OLED_DisplayTurn(1);	//0正常显示 1 屏幕翻转显示

	hal_key_init(KEY_ID_CENTER, 1000, 500, key_center_event_callback);
	hal_key_init(KEY_ID_LEFT, 1000, 500, key_left_event_callback);
	hal_key_init(KEY_ID_RIGHT, 1000, 500, key_right_event_callback);

	/**
	 * 显示 logo & slogan
	 */
	OLED_ShowPicture(10, 6, 108, 37, (uint8_t*) bmp_tws_logo, 1);
	OLED_ShowString(0, 50, (uint8_t*) "Technology", 8, 1);
	OLED_ShowString(64, 50, (uint8_t*) "with", 8, 1);
	OLED_ShowString(92, 50, (uint8_t*) "Spirit", 8, 1);

	//OLED_Clear_Area(18,8,8,8);
	OLED_Refresh();
	delay_ms(1000);

	OLED_Clear();

	return 0;
}

void gui_refresh(void)
{
	if (NULL == current_page)
		return;

	t_gui_obj *item = current_page->item_list_head;

	do
	{
		if (NULL != item)
		{
			if (item->status.is_changed)
			{
				item->draw(item);
			}

			item = item->next;
			if (item == current_page->item_list_head)
			{
				/* 遍历完一遍page的控件list */
				break;
			}
		}
		else
		{
			break;
		}
	} while (1);

	OLED_Refresh();
}

void gui_refresh_by_force(void)
{
	if (NULL == current_page)
		return;

	t_gui_obj *item = current_page->item_list_head;

	do
	{
		if (NULL != item)
		{
			item->draw(item);

			item = item->next;
			if (item == current_page->item_list_head)
			{
				/* 遍历完一遍page的控件list */
				break;
			}
		}
		else
		{
			break;
		}
	} while (1);

	OLED_Refresh();
}

/* 定时回调以实现事件传递 */
void gui_polling(uint16_t interval_in_ms)
{
	static uint16_t ms_passed = 0;
	ms_passed += interval_in_ms;

	hal_key_scan(interval_in_ms); /* 按键扫描 */
	if (ms_passed >= GUI_REFRESH_INTERVAL)
	{
		gui_refresh(); /* GUI刷新 */
	}
}

/* 用户线程调用以向外传递事件 */
void gui_event_handler(void)
{
	if (current_page != new_page)
	{
		/**
		 * 在此处真正的实现安全的异步切换页面
		 */

		if (NULL != current_page->exit)
		{
			current_page->exit(); /* 执行页面退出操作 */
		}
		//gui_page_dispose(current_page); /* 删除原先页面占用的资源 */

		if (NULL != new_page->enter)
		{
			new_page->enter(); /* 执行新页面进入操作 */
		}

		current_page = new_page;
		gui_refresh_by_force();
	}
	else if (NULL == current_page)
	{
		return;
	}

	t_gui_obj *item = current_page->item_list_head;

	/* 调用各事件回调 */
	do
	{
		if (NULL != item)
		{
			if (item->status.event_type)
			{
				if (NULL != item->event_cb)
				{
					item->event_cb(item->status.event_type);
				}

				item->status.event_type = 0;
			}

			item = item->next;
			if (item == current_page->item_list_head)
			{
				/* 遍历完一遍page的控件list */
				break;
			}
		}
		else
		{
			break;
		}
	} while (1);
}

void gui_obj_set_event_cb(t_gui_obj *obj, t_func_gui_event_cb cb)
{
	obj->event_cb = cb;
}

void gui_obj_delete(t_gui_obj *obj)
{
	free(obj);
}

/***************************** Page *******************************/

/**
 * 创建一个页面
 * @param title, 页面标题，可为NULL
 * @return Page handler
 */
t_gui_page* gui_page_create(const char *title)
{
	t_gui_page *page = (t_gui_page*) malloc(sizeof(t_gui_page));

	if (NULL == page)
	{
		return NULL;
	}

	page->title = title;
	page->item_list_head = NULL;
	page->item_current = NULL;
	page->enter = NULL;
	page->exit = NULL;

	return page;
}

void gui_page_dispose(t_gui_page *page)
{
	if (NULL == page)
	{
		return;
	}

	if (NULL != page->item_list_head)
	{
		t_gui_obj *temp = page->item_list_head;

		while (temp->next != page->item_list_head)
		{
			free(temp);
			temp = temp->next;
		}

		free(page->item_list_head);
	}

	free(page);
}

void gui_page_set_cb(t_gui_page *page, void (*enter)(void), void (*exit)(void))
{
	page->enter = enter;
	page->exit = exit;
}

void gui_page_change_to(t_gui_page *page)
{
	if ((NULL == page) || (current_page == page))
	{
		return;
	}

	new_page = page;
	if (new_page != current_page->parent)
	{
		/* 如果不是返回上一页，则将当前页设置为新页面的上一页 */
		new_page->parent = current_page;
	}

	OLED_Clear();
}

void gui_page_obj_add(t_gui_page *page, t_gui_obj *obj)
{
	if (NULL == page->item_list_head)
	{
		page->item_list_head = obj;
		page->item_current = obj;
		obj->prev = obj;
		obj->next = obj;

		if (obj->status.selectable)
		{
			page->item_list_head->status.is_selected = 1; /* 选中添加的第一个控件 */
		}
	}
	else
	{
		t_gui_obj *temp = page->item_list_head;

		/* 查找page下的最后一个控件 */
		while (page->item_list_head != temp->next)
		{
			temp = temp->next;
		}

		/* 将新控件添加到双向循环链表 */
		temp->next = obj;
		page->item_list_head->prev = obj;
		obj->prev = temp;
		obj->next = page->item_list_head;
	}
}

void gui_page_obj_next(t_gui_page *page)
{
	if (NULL != page->item_list_head)
	{
		t_gui_obj *temp = page->item_current;
		while (temp->next->status.selectable == 0) /* 如果下一个控件不可选中，则继续往下查找 */
		{
			temp = temp->next;
			if (temp == page->item_current)
			{
				return; /* 查询完一遍，全都不可选 */
			}
		}

		page->item_current->status.is_selected = 0;
		page->item_current->status.is_changed = 1;
		temp->next->status.is_selected = 1;
		temp->next->status.is_changed = 1;
		/* 只要列表非空，则next和prev指针就会指向下一个控件，因此不需要再做非空判断 */
		page->item_current = temp->next;
	}
}

void gui_page_obj_prev(t_gui_page *page)
{
	if (NULL != page->item_list_head)
	{
		t_gui_obj *temp = page->item_current;
		while (temp->prev->status.selectable == 0) /* 如果下一个控件不可选中，则继续往上查找 */
		{
			temp = temp->prev;
			if (temp == page->item_current)
			{
				return; /* 查询完一遍，全都不可选 */
			}
		}

		page->item_current->status.is_selected = 0;
		page->item_current->status.is_changed = 1;
		temp->prev->status.is_selected = 1;
		temp->prev->status.is_changed = 1;
		/* 只要列表非空，则next和prev指针就会指向下一个控件，因此不需要再做非空判断 */
		page->item_current = temp->prev;
	}
}

/***************************** Button *******************************/
static void gui_button_draw(t_gui_button *btn)
{
	if (btn->font_size > btn->h)
	{
		return;
	}

	//OLED_Clear_Cache(btn->x, btn->y, btn->w, btn->h);

//	/* 上横 */
//	OLED_DrawLine(btn->x, btn->y, btn->x + btn->w - 1, btn->y, btn->base.status.is_selected);
//	/* 左竖 */
//	OLED_DrawLine(btn->x, btn->y, btn->x, btn->y + btn->h - 1, btn->base.status.is_selected);
//	/* 下横 */
//	OLED_DrawLine(btn->x, btn->y + btn->h - 1, btn->x + btn->w - 1, btn->y + btn->h - 1, btn->base.status.is_selected);
//	/* 右竖 */
//	OLED_DrawLine(btn->x + btn->w - 1, btn->y, btn->x + btn->w - 1, btn->y + btn->h - 1, btn->base.status.is_selected);
//
//	/* 文字 */
//	OLED_ShowString(btn->x + 2, btn->y + (btn->h - 8) / 2, (uint8_t*)btn->text, btn->font_size, 1);

	if (btn->base.status.is_selected)
	{
		OLED_Fill_Cache(btn->x, btn->y, btn->w, btn->h);
		OLED_ShowString(btn->x + 2, btn->y + (btn->h - 8) / 2,
				(uint8_t*) btn->text, btn->font_size, 0);
	}
	else
	{
		OLED_Clear_Cache(btn->x, btn->y, btn->w, btn->h);

		/* 上横 */
		OLED_DrawLine(btn->x, btn->y, btn->x + btn->w - 1, btn->y, 1);
		/* 左竖 */
		OLED_DrawLine(btn->x, btn->y, btn->x, btn->y + btn->h - 1, 1);
		/* 下横 */
		OLED_DrawLine(btn->x, btn->y + btn->h - 1, btn->x + btn->w - 1,
				btn->y + btn->h - 1, 1);
		/* 右竖 */
		OLED_DrawLine(btn->x + btn->w - 1, btn->y, btn->x + btn->w - 1,
				btn->y + btn->h, 1);

		/* 文字 */
		OLED_ShowString(btn->x + 2, btn->y + (btn->h - 8) / 2,
				(uint8_t*) btn->text, btn->font_size, 1);
	}

	btn->base.status.is_changed = 0;

}

t_gui_obj* gui_button_create(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
		const char *text, uint8_t font_size)
{
	t_gui_button *btn = (t_gui_button*) malloc(sizeof(t_gui_button));

	if (NULL == btn)
	{
		return NULL;
	}

	memset(btn, 0, sizeof(t_gui_button));

	btn->base.status.is_changed = 1;
	btn->base.status.selectable = 1;
	btn->base.draw = (t_func_gui_draw*) gui_button_draw;

	btn->text = text;
	btn->font_size = font_size;
	btn->x = x;
	btn->y = y;
	btn->w = w;
	btn->h = h;

	return (t_gui_obj*) btn;
}

void gui_button_set_text(t_gui_obj *button, const char *text)
{
	t_gui_button *obj = (t_gui_button*) button;

	obj->text = text;
	obj->base.status.is_changed = 1;
}

/***************************** Label *******************************/
static void gui_label_draw(t_gui_button *lbl)
{
	if (lbl->font_size > lbl->h)
	{
		return;
	}

	OLED_Clear_Cache(lbl->x, lbl->y, lbl->w, lbl->h);

	/* 文字 */
	OLED_ShowString(lbl->x, lbl->y, (uint8_t*) lbl->text, lbl->font_size, 1);

	lbl->base.status.is_changed = 0;

}

t_gui_obj* gui_label_create(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
		const char *text, uint8_t font_size)
{
	t_gui_label *lbl = (t_gui_label*) malloc(sizeof(t_gui_label));

	if (NULL == lbl)
	{
		return NULL;
	}

	memset(lbl, 0, sizeof(t_gui_label));

	lbl->base.status.is_changed = 1;
	lbl->base.status.selectable = 0;
	lbl->base.draw = (t_func_gui_draw*) gui_label_draw;

	lbl->text = text;
	lbl->font_size = font_size;
	lbl->x = x;
	lbl->y = y;
	lbl->w = w;
	lbl->h = h;

	return (t_gui_obj*) lbl;
}

void gui_label_set_text(t_gui_obj *label, const char *text)
{
	t_gui_label *lbl = (t_gui_label*) label;

	lbl->text = text;
	lbl->base.status.is_changed = 1;
}

/***************************** Image *******************************/
static void gui_image_draw(t_gui_image *obj)
{
	OLED_ShowPicture(obj->x, obj->y, obj->w, obj->h, (uint8_t*) obj->img, 1);

	obj->base.status.is_changed = 0;

}

t_gui_obj* gui_image_create(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
		const char *img_src)
{
	t_gui_image *obj = (t_gui_image*) malloc(sizeof(t_gui_image));

	if (NULL == obj)
	{
		return NULL;
	}

	memset(obj, 0, sizeof(t_gui_image));

	obj->base.status.is_changed = 1;
	obj->base.status.selectable = 0;

	obj->base.draw = (t_func_gui_draw*) gui_image_draw;

	obj->img = img_src;
	obj->x = x;
	obj->y = y;
	obj->w = w;
	obj->h = h;

	return (t_gui_obj*) obj;
}

void gui_image_set_src(t_gui_obj *img, const char *img_src)
{
	t_gui_image *obj = (t_gui_image*) img;

	obj->img = img_src;
	obj->base.status.is_changed = 1;
}

/***************************** Image button *******************************/
t_gui_obj* gui_image_button_create(uint16_t x, uint16_t y, uint16_t w,
		uint16_t h, const char *img_src, const char *text, uint8_t font_size)
{
	t_gui_image_button *obj = (t_gui_image_button*) malloc(
			sizeof(t_gui_image_button));

	if (NULL == obj)
	{
		return NULL;
	}

	memset(obj, 0, sizeof(t_gui_image_button));

	obj->base.status.is_changed = 1;
	obj->base.status.selectable = 0;

	obj->base.draw = (t_func_gui_draw*) NULL;

	obj->img = img_src;
	obj->font_size = font_size;
	obj->text = text;

	return (t_gui_obj*) obj;
}
